package com.g4m.baselib.tool;

import org.apache.commons.lang3.StringUtils;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * 文件操作通用类
 * Created on 2020-01-01
 * <p>Copyright 2008-2020 snsndk.com</p>
 * <p>
 * hudesen[hudesen@hudesen.com]
 */
public final class FileUtil {
    private final static String EMPTY_STRING = "";
    private final static String DOT_STRING = ".";
    private final static String PATH_SPLIT_STRING = "/";

    /**
     * 生成临时文件路径
     *
     * @param prefix 前缀
     * @param suffix 后边缀
     * @return 临时文件路径
     * @throws IOException 异常
     */
    public static String createTmpFilePath(String prefix, String suffix) throws IOException {
        File file = File.createTempFile(prefix, suffix);
        file.delete();
        return file.getAbsolutePath();
    }


    /**
     * 判断一个文件是否存在
     *
     * @param filePath 文件路径
     * @return 判断结果
     */
    public static boolean isExists(String filePath) {
        if (StringUtils.isNotEmpty(filePath)) {
            File file = new File(filePath);
            return file.exists();
        }
        return false;
    }

    /**
     * 判断一个文件是否是目录
     *
     * @param filePath 文件路径
     * @return 判断结果
     */
    public static boolean isDir(String filePath) {
        if (StringUtils.isNotEmpty(filePath)) {
            File file = new File(filePath);
            return file.isDirectory();
        }
        return false;
    }

    /**
     * 判断一个文件是否是文件
     *
     * @param filePath 文件路径
     * @return 判断结果
     */
    public static boolean isFile(String filePath) {
        if (StringUtils.isNotEmpty(filePath)) {
            File file = new File(filePath);
            return file.isFile();
        }
        return false;
    }

    /**
     * 获取传入的文件名称的扩展名。
     * 如果文件名称不具有后缀或为空，则返回空字符串。
     *
     * @param fileName 要获取其扩展名的文件名称。
     * @return 传入的文件名称的扩展名。
     */
    public static String getExtension(String fileName) {
        fileName = getLocalFilePath(fileName);
        fileName = fileName.substring(fileName.lastIndexOf(PATH_SPLIT_STRING) + 1);
        // 如果文件名称为null或者空字符串，则抛出异常。
        if (StringUtils.isNotEmpty(fileName)) {
            // 如果文件名称不具有后缀，则返回空字符串。
            int index = fileName.lastIndexOf(DOT_STRING.charAt(0));
            if (index >= 0) {
                return fileName.substring(index).toLowerCase();
            }
        }
        return EMPTY_STRING;
    }

    /**
     * 修改文件名称的后缀。
     * 如果文件名称没有后缀，则返回新的文件名称加上新的后缀；否则返回将文件名称的后缀修改为新的后缀后的文件名称。
     *
     * @param fileName     要修改的文件名称。
     * @param newExtension 要修改为的后缀。
     * @return 修改后的文件名称。
     */
    public static String changeExtension(String fileName, String newExtension) {
        if (StringUtils.isEmpty(fileName)) {
            return EMPTY_STRING;
        }
        if (StringUtils.isEmpty(newExtension)) {
            return EMPTY_STRING;
        }
        if (DOT_STRING.equals(newExtension)) {
            return EMPTY_STRING;
        }

        String extension = newExtension.charAt(0) == DOT_STRING.charAt(0) ? newExtension
                : DOT_STRING.charAt(0) + newExtension;
        int index = fileName.lastIndexOf(DOT_STRING.charAt(0));
        if (index >= 0) {
            return fileName.substring(0, index) + extension;
        } else {
            return fileName + extension;
        }
    }

    /**
     * 移除文件名称后缀。
     *
     * @param fileName 要移除其后缀的文件名称。
     * @return 移除了后缀的文件名称。
     */
    public static String removeExtension(String fileName) {
        if (StringUtils.isNotEmpty(fileName)) {
            int index = fileName.lastIndexOf(DOT_STRING.charAt(0));
            if (index >= 0) {
                return fileName.substring(0, index);
            } else {
                return fileName;
            }
        }
        return EMPTY_STRING;
    }

    /**
     * 返回本地文件夹路径
     *
     * @param folderPath 文件夹路径。
     * @param fileName   文件名称。
     * @return 文件夹路径与文件名称组合后的新路径。
     */
    public static String getLocalFilePath(String folderPath, String fileName) {
        if (StringUtils.isNotEmpty(folderPath) && StringUtils.isNotEmpty(fileName)) {
            return getLocalFilePath(folderPath + PATH_SPLIT_STRING + fileName);
        }
        return EMPTY_STRING;
    }

    /**
     * 返回本地文件夹路径
     *
     * @param fileName 文件夹路径。
     * @return 文件夹路径与文件名称组合后的新路径。
     */
    public static String getLocalFilePath(String fileName) {
        if (StringUtils.isNotEmpty(fileName)) {
            String separator = PATH_SPLIT_STRING;
            fileName = fileName.replace("\\", separator);
            fileName = fileName.replace("//", separator);
            return fileName;
        }
        return EMPTY_STRING;
    }

    /**
     * 获取文件路径
     *
     * @param fileName 文件名
     * @return 文件路径
     */
    public static String getFilePath(String fileName) {
        if (StringUtils.isNotEmpty(fileName)) {
            fileName = getLocalFilePath(fileName);
            return fileName.substring(0, fileName.lastIndexOf(PATH_SPLIT_STRING));
        }
        return EMPTY_STRING;
    }

    /**
     * 优化后文件名
     *
     * @param fileName 文件名
     * @return 优化后文件名
     */
    public static String getFileName(String fileName) {
        if (StringUtils.isNotEmpty(fileName)) {
            String separator = System.getProperty("file.separator");
            if (StringUtils.isEmpty(separator)) {
                separator = PATH_SPLIT_STRING;
            }
            fileName = getLocalFilePath(fileName);
            return fileName.substring(fileName.lastIndexOf(separator) + 1);
        }
        return EMPTY_STRING;
    }

    /**
     * 取得在文件夹下所有文件列表
     *
     * @param filePath 文件夹路径
     * @return string[]文件数组列表
     */
    public static String[] getFileList(String filePath) {
        String[] list = null;
        if (StringUtils.isNotEmpty(filePath)) {
            File file = new File(filePath);
            if (file.isDirectory()) {
                list = file.list();
            }
        }
        return list;
    }

    /**
     * 取得在文件夹下所有文件列表
     *
     * @param filePath   文件夹路径
     * @param fileFilter 文件过滤
     * @return string[]文件数组列表
     */
    public static String[] getFileList(String filePath, FilenameFilter fileFilter) {
        String[] list = null;
        if (StringUtils.isNotEmpty(filePath) && fileFilter != null) {
            File file = new File(filePath);
            if (file.isDirectory()) {
                list = file.list(fileFilter);
            }
        }
        return list;
    }

    /**
     * 返回当前磁盘可用空间
     *
     * @param filePath 文件路径
     * @return 可用空间
     */
    public static long getDiskFreeSpace(String filePath) {
        if (StringUtils.isNotEmpty(filePath)) {
            File file = new File(filePath);
            return file.getFreeSpace();
        }
        return -1;
    }

    /**
     * 返回当前磁盘总空间
     *
     * @param filePath 文件路径
     * @return 当前磁盘总空间
     */
    public static long getDiskTotalSpace(String filePath) {
        if (StringUtils.isNotEmpty(filePath)) {
            File file = new File(filePath);
            return file.getTotalSpace();
        }
        return -1;
    }

    /**
     * 返回文件大小
     *
     * @param filePath 文件
     * @return 文件大小
     */
    public static long getFileSize(String filePath) {
        if (StringUtils.isNotEmpty(filePath)) {
            File file = new File(filePath);
            if (file.exists() && file.isFile() && file.canRead()) {
                return file.length();
            }
        }
        return -1;
    }


    /**
     * 创建单一目录
     *
     * @param path 要创建的目录
     * @return 创建结果
     */
    public static boolean mkdir(String path) {
        path = getFilePath(path);
        if (StringUtils.isNotEmpty(path)) {
            File file = new File(path);
            if (!file.exists()) {
                return file.mkdir();
            }
        }
        return true;
    }

    /**
     * 创建多级目录
     *
     * @param path 要创建的目录路径
     * @return 创建结果
     */
    public static boolean mkdirs(String path) {
        path = getFilePath(path);
        if (StringUtils.isNotEmpty(path)) {
            File file = new File(path);
            if (!file.exists()) {
                return file.mkdirs();
            }
        }
        return false;
    }

    /**
     * 搬移文件
     *
     * @param from 要搬移的文件路径
     * @param to   目标文件路径
     * @return 移动结果
     */
    public static boolean mv(String from, String to) {
        if (StringUtils.isNotEmpty(from) && StringUtils.isNotEmpty(to)) {
            File fromFile = new File(from);
            File toFile = new File(to);
            try {
                if (mkdirs(toFile.getParent())) {
                    return fromFile.renameTo(toFile);
                }
            } catch (Exception e) {
                return false;
            }
        }
        return false;
    }

    /**
     * 搬移文件,可控制是否强制搬移
     *
     * @param from  要搬移的文件路径
     * @param to    目标文件路径
     * @param force 是否强制搬移 true(强制) false(不强制)
     * @return 移动结果
     */
    public static boolean mv(String from, String to, boolean force) {
        if (StringUtils.isNotEmpty(from) && StringUtils.isNotEmpty(to)) {
            File toFile = new File(to);
            if (force && toFile.exists()) {
                if (!toFile.delete()) {
                    return false;
                }
            }
        }
        return mv(from, to);
    }

    /**
     * 复制文件
     *
     * @param from 源文件
     * @param to   目录文件
     * @return 结果
     */
    public static boolean cp(String from, String to) {
        if (StringUtils.isNotEmpty(from) && StringUtils.isNotEmpty(to)) {
            File fromFile = new File(from);
            File toFile = new File(to);
            mkdirs(toFile.getParent());
            try (FileInputStream fis = new FileInputStream(fromFile);
                 BufferedInputStream bufferIn = new BufferedInputStream(fis, 2024);
                 FileOutputStream fos = new FileOutputStream(toFile);
                 BufferedOutputStream bufferOut = new BufferedOutputStream(fos, 2024)) {
                int c;
                while ((c = bufferIn.read()) != -1) {
                    bufferOut.write(c);
                }
                return true;
            } catch (Exception e) {
                return false;
            }
        }
        return true;
    }

    /**
     * 复制文件,可控制是否强制搬移
     *
     * @param from  源文件
     * @param to    目录文件
     * @param force 是否强制搬移 true(强制) false(不强制)
     * @return 结果
     */
    public static boolean cp(String from, String to, boolean force) {
        if (StringUtils.isNotEmpty(to)) {
            File toFile = new File(to);
            if (force && toFile.exists()) {
                if (toFile.delete()) {
                    return cp(from, to);
                } else {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * 删除磁盘文件
     *
     * @param fileName 要删除的文件名
     * @return 删除结果
     */
    public static boolean delete(String fileName) {
        if (StringUtils.isEmpty(fileName)) {
            return true;
        } else {
            File file = new File(fileName);
            if (file.exists()) {
                return file.delete();
            }
        }
        return true;
    }

    /**
     * 将对象序列化入文件
     *
     * @param fileName 文件
     * @param obj      写入对象
     * @return 写入结果
     */
    public static boolean writeObjectToFile(String fileName, Object obj) {
        if (StringUtils.isEmpty(fileName) || obj == null) {
            return false;
        }
        mkdirs(getFilePath(fileName));
        try {
            ObjectOutputStream in = new ObjectOutputStream(new FileOutputStream(fileName));
            in.writeObject(obj);
            in.close();
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 将序列化的文件读入对象
     *
     * @param fileName 文件
     * @return Object 写入对象
     */
    public static Object readObjectFromFile(String fileName) {
        Object obj = null;
        if (StringUtils.isNotEmpty(fileName)) {
            try {
                ObjectInputStream in = new ObjectInputStream(new FileInputStream(fileName));
                obj = in.readObject();
                in.close();
            } catch (Exception ignore) {
            }
        }
        return obj;
    }

    /**
     * 读取文件为对象
     *
     * @param fileName 文件名
     * @param <T>      对象泛型
     * @return 读取结果
     * @throws Exception 读取异常
     */
    public static <T> T readFile2Obj(String fileName) throws Exception {
        try (InputStream input = new FileInputStream(fileName)) {
            return readStream2Obj(input);
        }
    }

    /**
     * 读取输入流为对象
     *
     * @param inputStream 输入流
     * @param <T>         对象泛型
     * @return 读取结果
     * @throws Exception 读取异常
     */
    @SuppressWarnings("unchecked")
    public static <T> T readStream2Obj(InputStream inputStream) throws Exception {
        try (ObjectInputStream stream = new ObjectInputStream(inputStream)) {
            return (T) stream.readObject();
        }
    }

    /**
     * 写出对象到文件(序列化)
     *
     * @param obj      需要写出的对象
     * @param fileName 文件名称
     * @throws Exception 写出异常
     */
    public static void writeObj2File(Serializable obj, String fileName) throws Exception {
        try (OutputStream out = new FileOutputStream(fileName)) {
            writeObj2Steam(obj, out);
        }
    }

    public static void writeString2File(String str, String fileName, String encode) throws Exception {
        File file = new File(fileName);
        if (!file.getParentFile().exists()) {
            if (!file.getParentFile().mkdirs()) {
                throw new IOException();
            }
        }
        try (Writer out = new OutputStreamWriter(new FileOutputStream(fileName), encode)) {
            out.write(str);
            out.flush();
        }
    }

    /**
     * 写出对象到输出流
     *
     * @param obj          需要写出的对象
     * @param outputStream 输出流
     * @throws Exception 写出异常
     */
    public static void writeObj2Steam(Serializable obj, OutputStream outputStream) throws Exception {
        ObjectOutputStream stream = new ObjectOutputStream(outputStream);
        stream.writeObject(obj);
        stream.flush();
    }

    /**
     * 从输入流中获取字节数组
     *
     * @param inputStream 流
     * @return 字节数组
     * @throws IOException Jayvee
     */
    public static byte[] readInputStream(InputStream inputStream) throws IOException {
        byte[] buffer = new byte[1024];
        int len = 0;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        while ((len = inputStream.read(buffer)) != -1) {
            bos.write(buffer, 0, len);
        }
        bos.close();
        return bos.toByteArray();
    }

    /**
     * 通过url下载文件数字
     *
     * @param urlStr 字符串
     * @return 字节数组
     * @throws IOException 异常
     */
    public static byte[] downLoadFromUrl(String urlStr) throws IOException {
        URL url = new URL(urlStr);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        InputStream inputStream = conn.getInputStream();
        //获取自己数组
        return readInputStream(inputStream);
    }
}
